﻿//////////////////////////////////////////////////////////////////////////////////
//
// Project            : XNAsteroids
// Module:            : XNAsteroidsGameObjects
// Description        : Asteroid game object.
// 
// Repository         : $URL$$
// Last changed by    : $LastChangedBy$
// Revision           : $LastChangedRevision$
// Last Changed       : $LastChangedDate$
// Author             : $Author$
//
// Id:                : $Id$
//
// Copyright:         (c) 2010 Torsten Bär
//
// Published under the MIT License. See license.txt or http://www.opensource.org/licenses/mit-license.php.
//
//////////////////////////////////////////////////////////////////////////////////

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace XNAsteroids.GameObjects.SpaceObjects
{
   /// <summary>
   /// An Asteroid.
   /// </summary>
   public class Asteroid : TexturedSpaceObject
   {
      #region Fields

      private const int SPLIT_COUNT = 10;

      private VertexPositionTexture[] model;
      private Random random;

      #endregion

      #region Properties

      /// <summary>
      /// Gets the rotation speed.
      /// </summary>
      public float RotationSpeed { get; private set; }

      /// <summary>
      /// Gets the radius.
      /// </summary>
      public float Radius { get; internal set; }

      /// <summary>
      /// Gets or sets the current rotation angle.
      /// </summary>
      public float Angle { get; set; }

      /// <summary>
      /// Indicates the rotation direction.
      /// </summary>
      public float RotationDirection { get; private set; }

      #endregion

      #region Constructors

      /// <summary>
      /// Initializes a new instance of the <see cref="Asteroid"/> class.
      /// </summary>
      /// <param name="game">The game.</param>
      /// <param name="position">The position.</param>
      /// <param name="radius">The radius.</param>
      /// <param name="effect">The effect.</param>
      public Asteroid(Game game, Vector3 position, float radius, BasicEffect effect)
         : base(game, position, effect)
      {
         Radius = radius;
         random = new Random();
      }

      #endregion

      #region Overrides

      /// <summary>
      /// Initializes the component. Override this method to load any non-graphics resources and query for any required services.
      /// </summary>
      public override void Initialize()
      {
         CreateModel();

         base.Initialize();
      }

      /// <summary>
      /// Called when graphics resources need to be loaded. Override this method to load any component-specific graphics resources.
      /// </summary>
      protected override void LoadContent()
      {
         base.LoadContent();

         Texture = Game.Content.Load<Texture2D>("rock_" + random.Next(1, 4));

         Effect.TextureEnabled = true;
         Effect.Texture = Texture;
         Effect.VertexColorEnabled = false;
      }

      /// <summary>
      /// Called when the GameComponent needs to be updated. Override this method with component-specific update code.
      /// </summary>
      /// <param name="gameTime">Time elapsed since the last call to Update</param>
      public override void Update(GameTime gameTime)
      {
         Angle += RotationDirection * (float)gameTime.ElapsedGameTime.TotalSeconds * RotationSpeed;

         if (Angle < 0)
            Angle += MathHelper.TwoPi;
         else if (Angle > MathHelper.TwoPi)
            Angle -= MathHelper.TwoPi;

         base.Update(gameTime);
      }

      /// <summary>
      /// Called when the DrawableGameComponent needs to be drawn. Override this method with component-specific drawing code. Reference page contains links to related conceptual articles.
      /// </summary>
      /// <param name="gameTime">Time passed since the last call to Draw.</param>
      public override void Draw(GameTime gameTime)
      {
         Effect.World =
            Matrix.CreateScale(Radius) *
            Matrix.CreateRotationZ(Angle) *
            Matrix.CreateTranslation(Position);

         base.Draw(gameTime);
      }

      /// <summary>
      /// Renders this instance.
      /// </summary>
      protected override void Render()
      {
         GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleFan, model, 0, model.Length - 2);
      }

      /// <summary>
      /// Initializes the bounding sphere.
      /// </summary>
      /// <returns></returns>
      protected override BoundingSphere CreateBoundingSphere()
      {
         return new BoundingSphere(Position, Radius);
      }

      /// <summary>
      /// Wraps the world depending on given bounding box.
      /// </summary>
      /// <param name="boundingBox">The bounding box.</param>
      public override void WorldWrap(BoundingBox boundingBox)
      {
         Vector3 add = new Vector3(Radius, Radius, 0);

         base.WorldWrap(new BoundingBox(Vector3.Subtract(boundingBox.Min, add), Vector3.Add(boundingBox.Max, add)));
      }

      #endregion

      #region Helper

      /// <summary>
      /// Creates the model.
      /// </summary>
      private void CreateModel()
      {
         model = new VertexPositionTexture[360 / SPLIT_COUNT + 2];
         model[0] = new VertexPositionTexture(Vector3.Zero, new Vector2(0.5f, 0.5f));

         for (int angle = 0, idx = 1; angle < 360; angle += SPLIT_COUNT, idx++)
         {
            Vector3 vertextPosition = new Vector3((float)Math.Cos(MathHelper.ToRadians(angle)),
                                                  (float)Math.Sin(MathHelper.ToRadians(angle)), 0);

            Vector2 texture = new Vector2(0.5f + vertextPosition.X * 0.5f,
                                          0.5f + vertextPosition.Y * 0.5f);

            model[idx] = new VertexPositionTexture(vertextPosition, texture);
         }
         model[model.Length - 1] = model[1];

         int next = 0;
         while (next == 0)
            next = random.Next(-1, 2);
         RotationDirection = next;

         RotationSpeed = 0.1f + (float)random.NextDouble() * 0.35f;
      }

      #endregion
   }
}